
package net.lab0.fractal.fractals;


import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import net.lab0.fractal.classes.ViewCoordinates;


/**
 * @author  116
 */
public class Mandelbrot
implements DisplayableFractal
{
    private int         maxIterations;
    private ViewCoordinates c;
    
    public Mandelbrot()
    {
        this.maxIterations = 512;
        this.c = new ViewCoordinates(0.0, 0.0, 2.0, 2.0);
    }
    
    public Mandelbrot(int maxIterations)
    {
        this.maxIterations = maxIterations;
    }
    
    public int getMaxIterations()
    {
        return maxIterations;
    }
    
    public void setMaxIterations(int maxIterations)
    {
        this.maxIterations = maxIterations;
    }
    
    @Override
    public BufferedImage renderImage(int width, int height)
    {
        int[][] pixels = computeFractal(width, height);
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        WritableRaster wr = bi.getRaster();
        
        float color[] = new float[4];
        color[3] = 255.0f;
        
        int minIter = pixels[0][0];
        int maxIter = pixels[0][0];
        for (int x = 0; x < width; ++x)
        {
            for (int y = 0; y < height; ++y)
            {
                if (pixels[x][y] < minIter)
                {
                    minIter = pixels[x][y];
                }
                else if (pixels[x][y] > maxIter)
                {
                    maxIter = pixels[x][y];
                }
            }
        }
        
        int gap = maxIter - minIter;
        for (int x = 0; x < width; ++x)
        {
            for (int y = 0; y < height; ++y)
            {
                color[0] = color[1] = color[2] = (float) (pixels[x][y] - minIter) * 255 / gap;
                wr.setPixel(x, y, color);
            }
        }
        
        return bi;
    }
    
    public int[][] computeFractal(int width, int height)
    {
        int[][] pixels = new int[width][height];
        
        ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(), 5L, TimeUnit.SECONDS,
        new LinkedBlockingQueue<Runnable>());
        
        ArrayList<MandelbrotRunnable> runnables = new ArrayList<MandelbrotRunnable>(height);
        for (int y = 0; y < height; y++)
        {
            MandelbrotRunnable runnable = new MandelbrotRunnable(this, width, height, y);
            runnables.add(runnable);
            executor.execute(runnable);
        }
        executor.shutdown();
        while (!executor.isTerminated())
        {
            try
            {
                Thread.sleep(100);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
        
        for (int y = 0; y < height; y++)
        {
            MandelbrotRunnable mt = runnables.get(y);
//            while (!mt.isFinished())
//            {
//                try
//                {
//                    Thread.sleep(100);
//                }
//                catch (InterruptedException e)
//                {
//                    // TODO Auto-generated catch block
//                    e.printStackTrace();
//                }
//            }
            for (int x = 0; x < width; ++x)
            {
                pixels[x][y] = mt.getPixels()[x];
            }
        }
        
        return pixels;
    }
    
    @Override
    public void setCoordinates(ViewCoordinates coords)
    {
        this.c = coords;
    }
    
    @Override
    public ViewCoordinates getCoordinates()
    {
        return c;
    }
}
